/**
 * @file solver.c
 *
 * @brief Problem solver.
 *
 * @date 1998 - 2010
 * @author Richard Delorme
 * @version 4.0
 */
#include "search.h"
#include "options.h"

int pv_check(const Board *init_board, Line *pv, Search *search);

/**
 * @brief Search observer.
 *
 * @param result Search result.
 */
static void solver_observer(Result *result)
{
	search_observer(result);
}

/**
 * @brief Problem set solver.
 *
 * Given a set of problems in a script file, solve the positions.
 *
 * @param search Search.
 * @param file_name Problems' file name.
 */
unsigned long long solver(Search *search, const char *file_name)
{
	Board board[1];
	char *problem, *string;
	FILE *file;
	unsigned long long T = 0, n_nodes = 0;
	int i, player;
	int n_err;
	char s[80];
	int previous_score;

	// add observers
	search_set_observer(search, solver_observer);
	search->options.verbosity = options.verbosity;

	// open script file with problems
	if (file_name == NULL) usage();
	file = fopen(file_name, "r");
	if (file == NULL) {
		fprintf(stderr, "solver: cannot open script file %s\n", file_name);
		exit(EXIT_FAILURE);
	}

	if (search->options.verbosity == 1) {
		if (search->options.header) printf(" # |%s\n", search->options.header);
		if (search->options.separator) printf("---+%s\n", search->options.separator);
	}

	// loop through problems
	for (i = 0; (string = string_read_line(file)) != NULL;) {
		problem = parse_skip_spaces(string);
		if (*problem == '%' || *problem == '\n' || *problem == '\r' || *problem == '\0') continue; //empty line
		player = board_set(board, problem);
		if (player == EMPTY) continue; // uncomplete board?

		++i;
		search_cleanup(search);
		search_set_board(search, board, player);
		search_set_level(search, options.level, search->n_empties);
		if (options.play_type == EDAX_TIME_PER_MOVE) search_set_move_time(search, options.time);
		else search_set_game_time(search, options.time);
		if (options.verbosity >= 2) {
			printf("\n*** problem # %d ***\n\n", i);
			board_print(search->board, search->player, stdout);
			putchar('\n');
			puts(search->options.header);
			puts(search->options.separator);
		}

		search->result->score = -SCORE_INF;
		do {
			previous_score = search->result->score;
			if (options.verbosity == 1) printf("%3d|", i);
				search_run(search);
			hash_exclude_move(search->pv_table, board_get_hash_code(search->board), search->result->move);
			hash_exclude_move(search->hash_table, board_get_hash_code(search->board), search->result->move);
			movelist_exclude(search->movelist, search->result->move);
		} while (options.all_best &&  search->result->score >= previous_score);

		T += search_time(search);
		n_nodes += search_count_nodes(search);
		if (options.verbosity) {
			fflush(stdout);
			if (options.verbosity >= 2) {
				puts(search->options.separator); putchar('\n');
			}
		}
		if (options.pv_check) {
			Line pv;
			line_copy(&pv, search->result->pv, 0);
			n_err = pv_check(board, &pv, search);
			if (n_err) {
				warn("Wrong pv:\n");
				board_print(board, player, stderr);
				fprintf(stderr, "setboard %s\nplay ", board_to_string(board, player, s));
				line_print(&pv, 200, " ", stderr);
				putc('\n', stderr); putc('\n', stderr);
			}
		}

		free(string);
	}
	if (options.verbosity == 1 && search->options.separator) printf("---+%s\n", search->options.separator);
	printf("%.30s : ", file_name);
	if (n_nodes) printf("%llu nodes in ", n_nodes);
	time_print(T, stdout);
	if (T > 0 && n_nodes > 0) printf(" (%8.0f nodes/s).", 1000.0 * n_nodes / T);
	printf("\n");

	// close files & free allocated data
	fclose(file);

	return n_nodes;
}

